package org.opens.websocket;

import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 功能描述
 *
 * @author: zhangyuming
 * @date: 2023/7/7 10:42
 */
@Component
@Slf4j
@ServerEndpoint(value = "/simple/websocket/{userId}/{serverId}")
public class SimpleWebsocket {

    private static RedisSocketClient redisSocketClient;

    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();

    public static final String REDIS_TOPIC_HANDLE_NAME = "websocketHandle";

    /**
     * websocket是多实例的, 需要通过这种静态属性字段注入的方式注入字段, 直接注入属性是无法注入的.
     * 参考: https://www.ngui.cc/el/3029836.html?action=onClick
     * jeecgboot的解决办法是是使用@Resource进行注入: 试了不行
     * 注意:
     * - websocket搭配redis的发布订阅主要是为了解决集群模式下的消息只能由持有channel的那个服务发送, 而redis的发布订阅会给所有的订阅者发送, 相
     * 当于是群发, 也就是说让每一个发送者都发送一次(代码中判断了没有对应的session就什么也不做), 但是只有持有channel的那个能发送成功.
     */
    @Autowired
    private void setRedisSocketClient(RedisSocketClient redisSocketClient) {
        SimpleWebsocket.redisSocketClient = redisSocketClient;
    }

    /**
     * 新建连接
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") String userId, @PathParam(value = "serverId") String serverId) {
        log.info("onOpen: {}, {}", userId, serverId);
        try {
            sessionPool.put(userId, session);
            log.info("【系统 WebSocket】有新的连接，总数为:" + sessionPool.size());
        } catch (Exception e) {
        }
    }

    /**
     * websocket发送消息
     */
    @OnMessage
    public void onMessage(String message, @PathParam(value = "userId") String userId, @PathParam(value = "serverId") String serverId) throws IOException {
        log.info("onMessage: {}, {}, {}", userId, serverId, message);
        sendMessageToRedis(userId, message);
    }

    @OnClose
    public void onClose(@PathParam("userId") String userId) {
        log.info("onClose: {}", userId);
        try {
            sessionPool.remove(userId);
            log.info("【系统 WebSocket】连接断开，总数为:" + sessionPool.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @OnError
    public void onError(Throwable throwable, @PathParam("userId") String userId) {
        log.info("onError: {}", throwable.getMessage());
    }

    public void pushMessage(String userId, String message) {
        for (Map.Entry<String, Session> item : sessionPool.entrySet()) {
            if (item.getKey().contains(userId)) {
                Session session = item.getValue();
                try {
                    //update-begin-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
                    synchronized (session){
                        log.info("【系统 WebSocket】推送单人消息:" + message);
                        session.getBasicRemote().sendText(message);
                    }
                    //update-end-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
                } catch (Exception e) {
                    log.error(e.getMessage(),e);
                }
            }
        }
    }

    public void sendMessageToRedis(String userId, String message) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("userId", userId);
        paramMap.put("message", message);
        redisSocketClient.sendMessage(REDIS_TOPIC_HANDLE_NAME, paramMap);
    }

}
